/**
 * 
 */
package com.wubo.sec.core;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;

import com.wubo.sec.filter.IFilterResourceLoader;

/**
 * 可用从数据库加载拦截配置的FilterInvocationSecurityMetadataSource
 * @author WuBo
 * @CreateDate 2012-2-14
 */
public class DBFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
	protected final Log logger = LogFactory.getLog(getClass());

    private Map<RequestMatcher, Collection<ConfigAttribute>> dbMap;
    
	private List<IFilterResourceLoader> filterResourceLoaders;
	
	private FilterInvocationSecurityMetadataSource metadataSource;

    //~ Constructors ===================================================================================================

    public DBFilterInvocationSecurityMetadataSource() {
    	
    }
    
    public void afterPropertiesSet() throws Exception {
    	refresh();
    }

    //~ Methods ========================================================================================================

    public Collection<ConfigAttribute> getAllConfigAttributes() {
        Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();

        if(metadataSource != null){
        	allAttributes.addAll(metadataSource.getAllConfigAttributes());
        }
        if(dbMap != null){
        	for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : dbMap.entrySet()) {
                allAttributes.addAll(entry.getValue());
            }
        }
        return allAttributes;
    }

    public Collection<ConfigAttribute> getAttributes(Object object) {
        final HttpServletRequest request = ((FilterInvocation) object).getRequest();
        
        if(metadataSource != null){
        	Collection<ConfigAttribute> attrs = metadataSource.getAttributes(object);
        	if(attrs != null){
        		return attrs;
        	}
        }
        if(dbMap != null){
	        for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : dbMap.entrySet()) {
	            if (entry.getKey().matches(request)) {
	                return entry.getValue();
	            }
	        }
        }
        return null;
    }

    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
	
	
    public void refresh(){
    	dbMap = buildRequestMap(loadFilterResources());
    }
    
	protected Map<RequestMatcher, Collection<ConfigAttribute>> buildRequestMap(Map<String, String> resourceMap) {
		Map<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
		
		for (Map.Entry<String, String> entry : resourceMap.entrySet()) {
			RequestMatcher requestMatcher = new AntPathRequestMatcher(entry.getKey());
        	ConfigAttribute attr = new SecurityConfig(entry.getValue());
        	
			if(requestMap.containsKey(requestMatcher)){
				requestMap.get(requestMatcher).add(attr);
			}else{
				Set<ConfigAttribute> attrs = new HashSet<ConfigAttribute>();
				attrs.add(attr);
				requestMap.put(requestMatcher, attrs);
			}
        }
        
        return requestMap;
    }
	
	public void setFilterResourceLoaders(
			List<IFilterResourceLoader> filterResourceLoaders) {
		this.filterResourceLoaders = filterResourceLoaders;
	}
	
	public void setMetadataSource(
			FilterInvocationSecurityMetadataSource metadataSource) {
		this.metadataSource = metadataSource;
	}

	private Map<String, String> loadFilterResources(){
		Map<String, String> resourceMap = new HashMap<String, String>();
		if(filterResourceLoaders != null){
			for(int i=0; i<filterResourceLoaders.size(); i++){
				resourceMap.putAll(filterResourceLoaders.get(i).loadFilterResource());
			}
		}
		return resourceMap;
	}
	
}
